using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using HCore.Unity.Core;
using HCore.Utility.Task;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Random = UnityEngine.Random;

namespace Game.Board
{
    /// <summary>
    ///     拼图板的信息
    /// </summary>
    public class Board
    {
        /// <summary>
        ///     保持游戏棋盘中幽灵像素的透明度水平。默认值设置为0.5，表示50%的不透明度。
        /// </summary>
        private const float _GHOST_TRANSPARENCY = 0.5f;


        /// <summary>
        ///     拼图块的大小
        /// </summary>
        public int TileSize { get; private set; }

        /// <summary>
        ///     拼图瓦片外圈填充值
        /// </summary>
        public int TilePadding { get; private set; }

        /// <summary>
        ///     拼图板实际宽度（包含了白色边框）
        /// </summary>
        public int Width { get; private set; }

        /// <summary>
        ///     拼图板实际高度（包含了白色边框）
        /// </summary>
        public int Height { get; private set; }

        /// <summary>
        ///     拼图的水平轴上到拼图块数量
        /// </summary>
        public int NumTileX { get; private set; }

        /// <summary>
        ///     拼图的垂直轴上到拼图块数量
        /// </summary>
        public int NumTileY { get; private set; }

        /// <summary>
        ///     拼图块数组
        /// </summary>
        public Tile.Tile[,] Tiles { get; private set; }

        /// <summary>
        ///     原始图片的有效纹理宽度
        /// </summary>
        public int EffectiveTextureWidth { get; private set; }

        /// <summary>
        ///     原始图片的有效纹理高度
        /// </summary>
        public int EffectiveTextureHeight { get; private set; }

        /// <summary>
        ///     自动矫正的最小距离。一旦拼图Tile和正确的位置距离一定位置，则自动吸附过去。
        /// </summary>
        public int MinMagnitude { get; private set; }

        /// <summary>
        ///     存储从图像文件加载的不透明精灵。它将用于创建棋盘的拼图。
        /// </summary>
        public Sprite Sprite { get; private set; }

        /// <summary>
        ///     存储基本精灵的透明版本，用于制作游戏棋盘的幽灵视图。
        /// </summary>
        public Sprite TransparentSprite { get; private set; }

        public Queue<QueueTask> SetupQueueTasks(Func<Texture2D> originalTextureFunc, int hopeTileSize, int hopeTileCount)
        {
            var queueTasks = new Queue<QueueTask>();

            queueTasks.Enqueue(TaskQueue.Wrap(subTaskExecute => InitBoardData(originalTextureFunc, hopeTileSize, hopeTileCount)));
            queueTasks.Enqueue(TaskQueue.Wrap(subTaskExecute => CreateRealSprite(originalTextureFunc), "制作拼图板"));
            queueTasks.Enqueue(TaskQueue.Wrap(subTaskExecute => CreateTransparentSprite()));
            queueTasks.Enqueue(TaskQueue.Wrap(CreateTiles, "制作拼图块"));

            return queueTasks;
        }

        /// <summary>
        ///     初始化拼图基础数据
        /// </summary>
        /// <param name="originalTextureFunc"></param>
        /// <param name="hopeTileSize"></param>
        /// <param name="hopeTileCount"></param>
        private void InitBoardData(Func<Texture2D> originalTextureFunc, int hopeTileSize, int hopeTileCount)
        {
            var originalTexture = originalTextureFunc();
            var originalTextureWidth = originalTexture.width;
            var originalTextureHeight = originalTexture.height;
            var tileSize = CalTIleSize(originalTexture.width, originalTexture.height, hopeTileSize, hopeTileCount);

            TileSize = tileSize;
            TilePadding = TileSize / 3;

            // 这个比值是有测试过的，比较合适。如果不行，可以再调整
            MinMagnitude = Math.Max(TileSize / 10, 10);

            // 计算纵和列有多少个 Tile（整数相除，可以自然的消除多余的长度）
            NumTileX = originalTextureWidth / TileSize;
            NumTileY = originalTextureHeight / TileSize;

            // 计算原始图片的有效宽度和高度
            EffectiveTextureWidth = NumTileX * TileSize;
            EffectiveTextureHeight = NumTileY * TileSize;

            // 计算拼图图片的实际宽度和高度
            Width = TilePadding + EffectiveTextureWidth + TilePadding;
            Height = TilePadding + EffectiveTextureHeight + TilePadding;

            Debug.Log("Start Create Board! \n" +
                      $"Tile Size: {tileSize}, \n" +
                      $"Padding: {TilePadding}, \n" +
                      $"Width: {Width}, \n" +
                      $"Height: {Height}, \n" +
                      $"Min Magnitude: {MinMagnitude}" +
                      $"NumTileX: {NumTileX}, \n" +
                      $"NumTileY: {NumTileY}, \n" +
                      $"EffectiveTextureWidth: {EffectiveTextureWidth}, \n" +
                      $"EffectiveTextureHeight: {EffectiveTextureHeight}, \n"
            );
        }

        /// <summary>
        ///     创建 拼图块
        /// </summary>
        /// <param name="subTaskExecute"></param>
        /// <returns></returns>
        private IEnumerator CreateTiles(Action<int, int> subTaskExecute)
        {
            // 准备执行进度数据
            var total = NumTileX * NumTileY;
            var step = 0;
            subTaskExecute.Invoke(step, total);


            // 构建 Tile 数据
            Debug.Log($"Start Setup Board！ -----  开始构建 {NumTileX * NumTileY}个 Tile 数据!, ThreadID : {Thread.CurrentThread.ManagedThreadId}");
            Tiles = new Tile.Tile[NumTileX, NumTileY];
            var realTexture = Sprite.texture;
            for (var xIndex = 0; xIndex < NumTileX; xIndex++)
            for (var yIndex = 0; yIndex < NumTileY; yIndex++)
                ///// 构建 Tile
            {
                var x = xIndex;
                var y = yIndex;
                if (step++ % 7 == 0) subTaskExecute.Invoke(step, total); // 回调执行进度数据
                yield return CreateTile(realTexture, x, y, tile => Tiles[x, y] = tile);
            }

            subTaskExecute.Invoke(total, total); // 回调执行进度数据

            yield return null;
        }


        /// <summary>
        ///     创建一个透明的拼图精灵
        /// </summary>
        /// <returns></returns>
        private IEnumerator CreateTransparentSprite()
        {
            Debug.Log($"Start Setup Board! ----  CreateTransparentSprite, ThreadID : {Thread.CurrentThread.ManagedThreadId}");

            var realTexture = Sprite.texture;
            var realTextureWidth = realTexture.width;
            var realTextureHeight = realTexture.height;

            // 创建一个新的 图透明纹理
            var newTex = new Texture2D(realTextureWidth, realTextureHeight, TextureFormat.ARGB32, false);
            var blockWidth = realTextureWidth - TilePadding * 2;
            var blockHeight = realTextureHeight - TilePadding * 2;

            var pixels = realTexture.GetPixels(TilePadding, TilePadding, blockWidth, blockHeight);

            var enumerable = pixels.Select(color =>
            {
                color.a = _GHOST_TRANSPARENCY;
                return color;
            });
            newTex.SetPixels(TilePadding, TilePadding, blockWidth, blockHeight, enumerable.ToArray());

            // 提交到 GPU 进行绘制
            newTex.Apply();

            // 根据新的纹理创建精灵
            TransparentSprite = GameUtils.CreateSprite(newTex, new Rect(0, 0, realTextureWidth, realTextureHeight));
            yield return null;
        }


        /// <summary>
        ///     此功能负责从提供的图像文件名中加载基本纹理，添加填充物，并从修改后的纹理创建精灵。
        /// </summary>
        /// <returns></returns>
        private IEnumerator CreateRealSprite(Func<Texture2D> originalTextureFunc)
        {
            Debug.Log($"Start Setup Board! ----  CreateRealSprite, ThreadID : {Thread.CurrentThread.ManagedThreadId}");

            // 添加一个边框填充，方便框出拼图。有左右、上下分别两个边框  // PNG 就是ARGB32 可以设置透明度
            var newTex = new Texture2D(Width, Height, TextureFormat.ARGB32, false);

            ///// 设置填充为白色
            var colors = Enumerable.Repeat(new Color(1, 1, 1, _GHOST_TRANSPARENCY), Width * Height);
            newTex.SetPixels(colors.ToArray());

            ///// 复制拼图像素
            var pixels = originalTextureFunc.Invoke().GetPixels(0, 0, EffectiveTextureWidth, EffectiveTextureHeight);
            newTex.SetPixels(TilePadding, TilePadding, EffectiveTextureWidth, EffectiveTextureHeight, pixels);

            ///// 提交到显卡内存，用来渲染
            newTex.Apply();

            Sprite = GameUtils.CreateSprite(newTex, new Rect(0, 0, Width, Height));

            yield return null;
        }

        /// <summary>
        ///     对于提供的图像，我们将用每个瓷砖大小的瓷砖将图像标记成行和列。
        ///     <ul>
        ///         <li>对于所有位于左侧边缘的瓷砖，或对于第0列，我们需要在Direction.LEFT中将曲线类型为NONE。这意味着对于左边缘的所有瓷砖，我们将有一条左方向的直线。</li>
        ///         <li>对于右、上和下方向，我们将随机选择POS或NEG曲线类型并应用。</li>
        ///         <li>同样，最后一列上的所有瓷砖都将右曲线类型为无。</li>
        ///         <li>顶行的所有瓷砖都将具有无的UP曲线类型。</li>
        ///         <li>底行的所有瓷砖都将具有DOWN操作为NONE。</li>
        ///     </ul>
        ///     由于我们将从左下到右上来度意化图像瓷砖的2d数组，我们将遵循以下规则。
        ///     <ul>
        ///         <li>对于最底部和最左侧的瓷砖，我们将左和下方向的曲线类型设置为直线。</li>
        ///         <li>对于右和顶部方向，我们将随机选择POS或NEG曲线类型。</li>
        ///         <li>
        ///             对于i列和j行索引i和j的所有其他内部瓷砖，我们将检查左侧的瓷砖，由i-1列表示，其右曲线类型，下面的瓷砖，由行j-1表示，其UP曲线类型。
        ///             然后，我们将分别将这种曲线类型的对瓷砖i和j的左方向和底方向应用与此曲线类型相反的方向。
        ///             我们需要这样做，以便瓷砖一旦根据模板贝塞尔曲线切割就适合。
        ///         </li>
        ///     </ul>
        /// </summary>
        /// <param name="originalTexture"></param>
        /// <param name="xIndex"></param>
        /// <param name="yIndex"></param>
        /// <param name="setTile"></param>
        /// <returns></returns>
        private IEnumerator CreateTile(Texture2D originalTexture, int xIndex, int yIndex, Action<Tile.Tile> setTile)
        {
            var tile = new Tile.Tile(TileSize, TilePadding, xIndex, yIndex, () => originalTexture);

            // Left side tiles.
            if (xIndex == 0)
            {
                tile.SetCurveType(Tile.Tile.Direction.Left, Tile.Tile.PosNegType.None);
            }
            else
            {
                // We have to create a tile that has LEFT direction opposite curve type.
                var leftTile = Tiles[xIndex - 1, yIndex];
                var rightOp = leftTile.GetCurveType(Tile.Tile.Direction.Right);
                tile.SetCurveType(Tile.Tile.Direction.Left, rightOp == Tile.Tile.PosNegType.Neg ? Tile.Tile.PosNegType.Pos : Tile.Tile.PosNegType.Neg);
            }

            // Bottom side tiles
            if (yIndex == 0)
            {
                tile.SetCurveType(Tile.Tile.Direction.Down, Tile.Tile.PosNegType.None);
            }
            else
            {
                var downTile = Tiles[xIndex, yIndex - 1];
                var upOp = downTile.GetCurveType(Tile.Tile.Direction.Up);

                tile.SetCurveType(Tile.Tile.Direction.Down, upOp == Tile.Tile.PosNegType.Neg ? Tile.Tile.PosNegType.Pos : Tile.Tile.PosNegType.Neg);
            }

            // Right side tiles.
            if (xIndex == NumTileX - 1)
            {
                tile.SetCurveType(Tile.Tile.Direction.Right, Tile.Tile.PosNegType.None);
            }
            else
            {
                var toss = Random.Range(0f, 1f);
                tile.SetCurveType(Tile.Tile.Direction.Right, toss < 0.5f ? Tile.Tile.PosNegType.Pos : Tile.Tile.PosNegType.Neg);
            }

            // Up side tile.
            if (yIndex == NumTileY - 1)
            {
                tile.SetCurveType(Tile.Tile.Direction.Up, Tile.Tile.PosNegType.None);
            }
            else
            {
                var toss = Random.Range(0f, 1f);
                tile.SetCurveType(Tile.Tile.Direction.Up, toss < 0.5f ? Tile.Tile.PosNegType.Pos : Tile.Tile.PosNegType.Neg);
            }

            setTile.Invoke(tile);

            tile.Apply();

            yield return null;
        }

        /// <summary>
        ///     - 如果没有期望的拼图块数量，则直接返回期望的拼图块<p />
        ///     - 如果有指定拼图片数，则根据期望的数量自动计算拼图块的尺码
        /// </summary>
        /// <param name="rectWidth">拼图宽</param>
        /// <param name="rectHeight">拼图高</param>
        /// <param name="hopeTileSize">期望拼图块的尺寸</param>
        /// <param name="hopeTileCount">期望拼图块的数量，如果不填或者小于等于0，则直接返回 hopeTileSize</param>
        /// <returns></returns>
        private static int CalTIleSize(int rectWidth, int rectHeight, int hopeTileSize, int hopeTileCount = 0)
        {
            // 如果没有期望的拼图块数量，则直接返回期望的拼图块
            if (hopeTileCount <= 0) return hopeTileSize;

            // 如果有指定拼图片数，则根据期望的数量自动计算拼图块的尺码
            var tileSize = (int)Mathf.Max(Mathf.Sqrt(hopeTileCount), 30);
            do
            {
                tileSize++;
            } while (rectWidth / tileSize * (rectHeight / tileSize) > hopeTileCount);

            // tileSize--;

            return tileSize;
        }

        #region 受控列表

        /// <summary>
        ///     受控的 Tile 索引。一旦固定，或者加入某个分组，则移出受控列表
        /// </summary>
        public IReadOnlyList<Vector2Int> ControlTiles { get; } = new List<Vector2Int>();

        /// <summary>
        ///     移除受控制Tile的索引
        /// </summary>
        public bool TryRemoveControlTile(Vector2Int vec)
        {
            return ControlTiles.Contains(vec) && ((List<Vector2Int>)ControlTiles).Remove(vec);
        }

        /// <summary>
        ///     添加受控制的Tile索引
        /// </summary>
        /// <param name="tileIndex"></param>
        public void AddControlTile(Vector2Int tileIndex)
        {
            ((List<Vector2Int>)ControlTiles).Add(tileIndex);
        }

        /// <summary>
        ///     排序， 更友好的在Game界面显示
        /// </summary>
        public void SortControlTiles()
        {
            ((List<Vector2Int>)ControlTiles).Sort((a, b) =>
            {
                var v = a.y.CompareTo(b.y);
                if (v == 0) return a.x.CompareTo(b.x);
                return -v;
            });
        }

        #endregion
    }
}